package com.xiaobingby.auth.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.xiaobingby.common.util.R;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.error.DefaultWebResponseExceptionTranslator;
import org.springframework.security.oauth2.provider.error.WebResponseExceptionTranslator;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;

import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;

/**
 * @author XiaoBingBy
 * @date 2018-08-26 12:00
 * @since 1.0
 */
@Configuration
@EnableAuthorizationServer // 开启 Authorization Server 的功能，以 Bean 形式注入 Spring IoC 容器中
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private RedisConnectionFactory redisConnectionFactory;

    @Autowired
    private ObjectMapper objectMapper;

    @Autowired
    private HttpServletResponse response;

    /**
     * http://localhost:1024/oauth/authorize?response_type=code&client_id=bing&scope=all&redirect_uri=http://localhost:8080
     * 配置客户端信息
     *
     * @param clients
     * @throws Exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                .withClient("bing")
                .resourceIds("bingUpmsResourceIds")
                .secret("{noop}123")
                .authorizedGrantTypes("password", "authorization_code", "refresh_token")
                .redirectUris("http://localhost:8080")
                .scopes("all");
    }

    /**
     * Spring Security 授权表达式
     * 配置 Token 节点的安全策略
     *
     * @param security
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security/*oauthServer*/) throws Exception {
        security//.tokenKeyAccess("permitAll()") /// url:/oauth/token_key,exposes
                /// public key for token
                /// verification if using
                /// JWT tokens
                .checkTokenAccess("isAuthenticated()") // url:/oauth/check_token
                // allow check token
                .allowFormAuthenticationForClients();
    }

    /**
     * 配置授权 Token 的节点和 Token 服务
     *
     * @param endpoints
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints
                // token存储
                .tokenStore(redisTokenStore())
                // 自定义token生成方案
                .tokenEnhancer(tokenEnhancer())
                // 身份认证管理器, 主要用于"password"授权模式
                .authenticationManager(authenticationManager)
                .userDetailsService(userDetailsService);
        // 登陆异常自定义返回
        endpoints.exceptionTranslator(webResponseExceptionTranslator());
    }

    /**
     * 使用redisTokenStore存储token
     *
     * @return
     */
    @Bean
    public TokenStore redisTokenStore() {
        RedisTokenStore tokenStore = new RedisTokenStore(redisConnectionFactory);
        tokenStore.setPrefix("bing_token:"); // 自定义Redis前缀
        return tokenStore;
    }

    /**
     * 用于扩展 token
     *
     * @return
     */
    @Bean
    public TokenEnhancer tokenEnhancer() {
        return (accessToken, authentication) -> {
            final Map<String, Object> additionalInfo = new HashMap<>(1);
            additionalInfo.put("license", "XiaoBingBy");
            ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
            return accessToken;
        };
    }

    /**
     * 登陆异常自定义返回
     *
     * @return
     */
    @Bean
    public WebResponseExceptionTranslator webResponseExceptionTranslator() {
        return new DefaultWebResponseExceptionTranslator() {

            @Override
            public ResponseEntity<OAuth2Exception> translate(Exception e) throws Exception {
                ResponseEntity<OAuth2Exception> translate = super.translate(e);
                OAuth2Exception body = translate.getBody();
                response.setHeader("Cache-Control", "no-store");
                response.setHeader("Pragma", "no-cache");
                response.setContentType("application/json;charset=UTF-8");
                response.setStatus(translate.getStatusCodeValue());
                response.getWriter().write(objectMapper.writeValueAsString(R.error(translate.getStatusCodeValue(), body, body.getMessage())));
                response.getWriter().close();
                return null;
            }

        };
    }

}
